from typing import Optional
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy_crud_plus import CRUDPlus

from modules.system.models.user import User
from modules.system.schemas.user_schemas import UserCreateDB, UserUpdateDB


class UserRepository(CRUDPlus[User]):
    """
    用户CRUD操作仓库
    """
    async def get_by_id(self, db: AsyncSession, pk: int) -> Optional[User]:
        """
        根据ID获取用户
        :param db: 数据库会话
        :param pk: 用户ID
        :return: 用户对象（如果存在）
        """
        return await self.select_model(db, pk)

    async def get_by_username(self, db: AsyncSession, username: str) -> Optional[User]:
        """
        根据用户名获取用户
        :param db: 数据库会话
        :param username: 用户名
        :return: 用户对象（如果存在）
        """
        res = await db.execute(select(User).where(User.username == username))
        return res.scalar_one_or_none()

    async def create(self, db: AsyncSession, data: UserCreateDB) -> User:
        """
        创建用户
        :param db: 数据库会话
        :param data: 用户创建数据
        :return: 创建后的用户对象
        """
        return await self.create_model(db, data)

    async def update(self, db: AsyncSession, pk: int, data: UserUpdateDB) -> Optional[User]:
        """
        更新用户信息
        :param db: 数据库会话
        :param pk: 用户ID
        :param data: 更新数据
        :return: 更新后的用户对象
        """
        await self.update_model(db, pk, data)
        return await self.select_model(db, pk)

    async def delete(self, db: AsyncSession, pk: int) -> bool:
        """
        删除用户
        :param db: 数据库会话
        :param pk: 用户ID
        :return: 是否删除成功
        """
        return await self.delete_model(db, pk)

    async def get_with_relation(self, db: AsyncSession, pk: int) -> Optional[User]:
        """
        获取用户及其关联角色、菜单、数据范围等信息
        :param db: 数据库会话
        :param pk: 用户ID
        :return: 用户对象（包含关联信息）
        """
        return await self.select_model_by_column(
            db,
            load_options=[
                selectinload(self.model.roles).options(
                    selectinload(Role.menus),
                    selectinload(Role.scopes).options(selectinload(DataScope.rules)),
                )
            ],
            load_strategies=['dept'],
            id=pk,
        )

user_dao = UserRepository(User)